<?php


namespace App\Services\drive;


use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
 * 带重试机制的Guzzle请求
 * @package App\Services\drive
 */
class GuzzleHttpDriver
{
    /**
     * 自动重试的请求（重试一次）
     * 使用示例：GuzzleHttpDriver::requestWithRetry('http://domain/api/user/v3/common/guzzleData', ['id'=>128], [40000, 'code']);
     * @param $url
     * @param array $params
     * @param array $retryCondition
     * @return \Psr\Http\Message\StreamInterface
     */
    public static function requestWithRetry($url, $params=[], $retryCondition=[40000, 'code'])
    {
        /*$client = new Client([
            // Base URI is used with relative requests
            'base_uri' => 'http://httpbin.org',
            // You can set any number of default request options.
            'timeout'  => 2.0,
        ]);
        $response = $client->request('GET', 'test');*/
        // $client = new Client();

        $stack = new HandlerStack();
        $stack->setHandler(new CurlHandler());

        list($retryConditionErrorCode, $retryConditionErrorCodeField) = $retryCondition;
        $stack->push(static::getRetryMiddleWare($retryConditionErrorCode, $retryConditionErrorCodeField));
        $client = new Client(['handler' => $stack]);


        $response = $client->get($url, $params);
        // $response = $client->get('http://localhost');
        $body = $response->getBody();
        // Implicitly cast the body to a string and echo it
        return $body;
        // echo $body;
    }

    /**
     * 指定错误码
     * @param array $retryConditionErrorCode 遇到什么错误码重试
     * @param string $retryConditionErrorCodeField 错误码字段
     * @return callable
     */
    protected static function getRetryMiddleWare($retryConditionErrorCode=[40001, 40014, 42001], $retryConditionErrorCodeField='errcode')
    {
        $retryConditionErrorCode = is_array($retryConditionErrorCode)?$retryConditionErrorCode:[$retryConditionErrorCode];
        return Middleware::retry(function (
            $retries,
            RequestInterface $request,
            ResponseInterface $response = null
        ) use ($retryConditionErrorCode, $retryConditionErrorCodeField) {
            // Limit the number of retries to 2
            if ($retries < 1 && $response && $body = $response->getBody()) {
                // Retry on server errors
                $response = json_decode($body, true);

                if (!empty($response[$retryConditionErrorCodeField]) && in_array(abs($response[$retryConditionErrorCodeField]), $retryConditionErrorCode, true)) {
                    info("Retrying with {$retryConditionErrorCodeField}=".implode(',', $retryConditionErrorCode));
                    return true;
                }
            }

            return false;
        }, function () {
            return 50; // 500ms
        });
    }
}